package com.qianyitian.hope2.analyzer.service;

import com.alibaba.fastjson.JSON;
import com.qianyitian.hope2.analyzer.model.*;
import com.qianyitian.hope2.analyzer.util.Utils;
import com.qianyitian.hope2.sdk.funds.FundBar;
import com.qianyitian.hope2.sdk.funds.FundInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class DDUService {
    public static final String STOCK_STORE_KEY = "stock-ddu-hash";
    public static final String FUND_STORE_KEY = "fund-ddu-hash";

    public static final String STOCK_REPORT_KEY = "stock-ddu-report";
    public static final String FUND_REPORT_KEY = "fund-ddu-report";

    private Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private StockService favoriteStockService;


    public int refreshStockDDU() {
        logger.info("start calculating each stock ddu result");
        SymbolList symbols = favoriteStockService.getSymbols(null);

        for (Stock symbol : symbols.getSymbols()) {
            try {
                Stock stock = favoriteStockService.getStockDailySA(symbol.getCode());
                DDU ddu = getDDUResult(stock);
                storeEachStockDDU(symbol.getCode(), ddu);
            } catch (Exception e) {
                logger.error(symbol.getCode() + "refresh stock ddu error", e);
            }
        }
        logger.info("end calculating each stock ddu range");

        logger.info("start calculating all stock ddu");
        int size = calcStockDDUReport();
        logger.info("end calculating all stock ddu");
        return size;
    }

    public int refreshFundDDU() {
        logger.info("start calculating each fund ddu result");
        List<Stock> funds = favoriteStockService.getSymbols("FUNDS").getSymbols();

        for (Stock fund : funds) {
            try {
                String content = favoriteStockService.getFundDetail(fund.getCode());
                FundInfo fundInfo = JSON.parseObject(content, FundInfo.class);
                DDU ddu = getDDUResult(fundInfo);
                storeEachFundDDU(fund.getCode(), ddu);
            } catch (Exception e) {
                logger.error(fund.getCode() + "refresh fund DDU error", e);
            }


        }
        logger.info("end calculating each fund ddu range");
        logger.info("start calculating all fund ddu");
        int size = calcFundDDUReport();
        logger.info("end calculating all fund ddu");
        return size;
    }

    private DDU getDDUResult(FundInfo fundInfo) {
        double range5 = calcRangeWithDays(fundInfo, 5);
        double range10 = calcRangeWithDays(fundInfo, 10);
        double range20 = calcRangeWithDays(fundInfo, 20);
        double range30 = calcRangeWithDays(fundInfo, 30);
        double range60 = calcRangeWithDays(fundInfo, 60);
        DDU ddu = new DDU();
        ddu.setCode(fundInfo.getCode());
        ddu.setName(fundInfo.getName());
        ddu.setDdu5(dduString(range5));
        ddu.setDdu10(dduString(range10));
        ddu.setDdu20(dduString(range20));
        ddu.setDdu30(dduString(range30));
        ddu.setDdu60(dduString(range60));

        return ddu;
    }

    private String dduString(double range) {
        return range >= 0.0D ? "Y" : "";
    }

    private void clearRedisSet() {
//        Cursor<String> cursor = redisTemplate.opsForSet().scan("dduHash", ScanOptions.NONE);
//        while (cursor.hasNext()) {
//            String member = cursor.next();
//            redisTemplate.opsForSet().remove("dduHash", member);
//        }
    }


    private DDU getDDUResult(Stock stock) {
        double range5 = calcRangeWithDays(stock, 5);
        double range10 = calcRangeWithDays(stock, 10);
        double range20 = calcRangeWithDays(stock, 20);
        double range30 = calcRangeWithDays(stock, 30);
        double range60 = calcRangeWithDays(stock, 60);
        DDU ddu = new DDU();
        ddu.setCode(stock.getCode());
        ddu.setName(stock.getName());
        ddu.setDdu5(dduString(range5));
        ddu.setDdu10(dduString(range10));
        ddu.setDdu20(dduString(range20));
        ddu.setDdu30(dduString(range30));
        ddu.setDdu60(dduString(range60));
        return ddu;
    }

    private double calcRangeWithDays(Stock stock, int days) {
        if (stock.getkLineInfos().size() < days + 1) {
            return 0;
        }
        KLineInfo startK = stock.getkLineInfos().get(stock.getkLineInfos().size() - days - 1);
        KLineInfo endK = stock.getkLineInfos().get(stock.getkLineInfos().size() - 1);
        double range = Utils.calcRange(startK.getClose(), endK.getClose());
        return range;
    }

    private double calcRangeWithDays(FundInfo fundInfo, int days) {
        if (fundInfo.getFundBarList().size() < days + 1) {
            return 0;
        }
        FundBar startK = fundInfo.getFundBarList().get(fundInfo.getFundBarList().size() - days - 1);
        FundBar endK = fundInfo.getFundBarList().get(fundInfo.getFundBarList().size() - 1);
        double range = Utils.calcRange(startK.getValue(), endK.getValue());
        return range;
    }


    private void storeEachStockDDU(String code, DDU ddu) {
        String s = JSON.toJSONString(ddu);
        redisTemplate.opsForHash().put(STOCK_STORE_KEY, code, s);
    }

    private void storeEachFundDDU(String code, DDU ddu) {
        String s = JSON.toJSONString(ddu);
        redisTemplate.opsForHash().put(FUND_STORE_KEY, code, s);
    }

    private int calcStockDDUReport() {
        List list = redisTemplate.opsForHash().values(STOCK_STORE_KEY);

        List<DDU> dduList = (List<DDU>) list.stream().map(item -> {
            String stock = (String) item;
            DDU ddu = JSON.parseObject(stock, DDU.class);
            return ddu;
        }).collect(Collectors.toList());


        Map map = new HashMap();
        map.put("items", dduList);
        map.put("generateTime", LocalDateTime.now().toString());
        String content = JSON.toJSONString(map);
        redisTemplate.opsForValue().set(STOCK_REPORT_KEY, content);
        return dduList.size();
    }

    private int calcFundDDUReport() {
        List list = redisTemplate.opsForHash().values(FUND_STORE_KEY);

        List<DDU> dduList = (List<DDU>) list.stream().map(item -> {
            String stock = (String) item;
            DDU ddu = JSON.parseObject(stock, DDU.class);
            return ddu;
        }).collect(Collectors.toList());


        Map map = new HashMap();
        map.put("items", dduList);
        map.put("generateTime", LocalDateTime.now().toString());
        String content = JSON.toJSONString(map);
        redisTemplate.opsForValue().set(FUND_REPORT_KEY, content);
        return dduList.size();
    }

    public String getAllStockDDU() {
        String content = redisTemplate.opsForValue().get(STOCK_REPORT_KEY);
        return content;
    }

    public String getAllFundDDU() {
        String content = redisTemplate.opsForValue().get(FUND_REPORT_KEY);
        return content;
    }

    private double score(int index, int total) {
        return Utils.get2Double((double) index * 100 / total);
    }


}
